package frame.laxcen;

import frame.laxcen.exception.IllegalFrameFormatException;
import frame.laxcen.response.BaseResponse;
import frame.laxcen.response.annotation.FrameFeature;
import frame.laxcen.util.Algorithm;
import frame.laxcen.util.FrameReader;
import frame.util.ByteUtil;
import frame.util.Ref;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

// 数据帧的基类
public class BaseFrame {

    // 完整的帧数据
    public byte[] FrameBytes;

    // 帧头
    public byte FrameHead;

    // 控制字
    public ControlWord CW;

    // 数据长度
    public int FrameDataLength;

    // 数据参数
    public byte[] FrameData;

    // CRC校验值
    public byte[] CRC;

    // 用于CRC校验的帧数据
    public byte[] CRCData;

    protected  BaseFrame() { }

    // 从byte数组中构造指令帧
    // param: bytes 使用改byte数组执行构造
    // throws: 构造出错时返回帧结构非法异常
    public BaseFrame(byte[] bytes) throws IllegalFrameFormatException  {

        initialize(bytes);
    }

    // 使用byte数组初始化指令帧
    // param: bytes 使用改byte数组执行初始化
    // throws: 构造出错时返回帧结构非法异常
    protected BaseFrame initialize(byte[] bytes) throws IllegalFrameFormatException {

        FrameReader reader = new FrameReader(bytes);

        try {

            this.FrameBytes = bytes;

            this.FrameHead = reader.readU8();

            this.CW = new ControlWord(ByteUtil.shortToBytes(reader.readU16()));

            this.FrameDataLength = Short.toUnsignedInt(reader.readU16());

            this.FrameData = reader.readU8n(this.FrameDataLength);

            this.CRC = ByteUtil.shortToBytes(reader.readU16());

            this.CRCData = Arrays.copyOfRange(bytes, 1, bytes.length - 2);

        } catch (Exception e) {

            e.printStackTrace();

            throw new IllegalFrameFormatException(e.toString());
        }

        if(this.FrameHead != (byte) 0xaa) {

            throw new IllegalFrameFormatException("FrameHead was expected to 0XAA.");
        }

        if(!Algorithm.checkCRC(this.CRCData, this.CRC)) {

            throw new IllegalFrameFormatException("CRC check failed.");
        }

        return this;
    }

    // 使用类名的命名规范检测该帧是否是对应反馈帧 对特殊的返回帧需要重写修正逻辑
    // param: responseType 指定反馈帧的类
    // return: 检测的结果
    public boolean isResponse(Class<? extends BaseResponse> responseType) {

        FrameFeature[] frameFeatures = responseType.getAnnotationsByType(FrameFeature.class);

        return frameFeatures.length != 0 && Arrays.stream(frameFeatures).anyMatch((feature) -> Byte.compare(CW.MessageType, feature.MessageType()) == 0 && Byte.compare(CW.MID, feature.Mid()) == 0 && CW.IsUpload == feature.IsUpload());
    }

    // 使用反射技术尝试将该实例转化为某一特定类型的Response
    // param: responseType 某一Response类型类
    // return: 如果转化成功 返回该Response类型的引用 否则返回空引用
    public <T extends BaseResponse> Ref<T> castToResponse(Class<T> responseType) {

        try {

            Constructor<T> constructor = responseType.getConstructor(BaseFrame.class);

            constructor.setAccessible(true);

            return Ref.of(constructor.newInstance(this));

        } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {

            e.printStackTrace();

            return Ref.empty();
        }
    }

    // 使用通信协议规范检测该帧是否是错误反馈帧
    // return: 检测的结果
    public boolean isErrorResponse() {

        return (CW.MessageType == 0) && CW.IsUpload && (CW.MID == 0);
    }

    // 控制字
    public static class ControlWord {

        // 消息ID 对应低8位
        public final byte MID;

        // 消息类型 对应高8位中的后4位
        public final byte MessageType;

        // 上传标志 对应高8位中的第4位
        public final boolean IsUpload;

        private ControlWord(byte[] data) {

            this.MID = data[1];

            this.MessageType = (byte) (data[0] & 0x0f);

            this.IsUpload = ByteUtil.isNotZeroBit(data[0], 4);
        }
    }
}
